#include "Application.h"
#include "third_party/assimp/Importer.hpp"
#include "third_party/assimp/scene.h"
#include "third_party/assimp/postprocess.h"
#include <functional>
#include "BBox.h"

#if defined(DEBUG) | defined(_DEBUG)
#pragma comment(lib, "assimpd.lib")
#else
#pragma comment(lib, "assimp.lib")
#endif


LineVertex lineVerts[]={
	{XMFLOAT4(0,0,0,1)},
	{XMFLOAT4(2,2,2,1)},
};

UINT lineIndex[] = {
	0,1
};

int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ){

#if defined(DEBUG) | defined(_DEBUG)
	AllocConsole();
	freopen("CONIN$", "r+t", stdin);
	freopen("CONOUT$", "w+t", stdout);
#endif

	Application app(hInstance, nCmdShow, 1280, 768);

	//assimpprocess
	Assimp::Importer importer;
	const aiScene* scene = importer.ReadFile(
		"D:\\test\\test.obj",
		aiProcess_Triangulate					|
		aiProcess_SortByPType					|
		aiProcess_ConvertToLeftHanded
		);

	if(!scene)	MessageBox(NULL, L"can't load scene.", NULL, NULL);

	std::function<void(aiNode*)> loadRecursively = [&loadRecursively, &app, scene](aiNode* root)mutable{
		if(root == nullptr) return;
		for(UINT i = 0; i < root->mNumMeshes; i++){
			auto meshIndex = root->mMeshes[i];
			auto pMesh = scene->mMeshes[meshIndex];
			auto pIndexData = new UINT[pMesh->mNumFaces*3];
			auto pVertData = new MeshVertex[pMesh->mNumVertices];
			//copy vectex
			BBox box;
			if(pMesh->HasPositions())
				for(UINT v = 0; v < pMesh->mNumVertices; v++){
					pVertData[v].v.x = pMesh->mVertices[v].x;
					pVertData[v].v.y = pMesh->mVertices[v].y;
					pVertData[v].v.z = pMesh->mVertices[v].z;
					pVertData[v].v.w = 1;
					box &= XMLoadFloat4(&(pVertData[v].v));
				}
			//copy normal
			if(pMesh->HasNormals())
				for(UINT v = 0; v < pMesh->mNumVertices; v++){
					pVertData[v].n.x = pMesh->mNormals[v].x;
					pVertData[v].n.y = pMesh->mNormals[v].y;
					pVertData[v].n.z = pMesh->mNormals[v].z;
					pVertData[v].n.w = 0;
				}
			//copy texcoord in channel 0
			if(pMesh->HasTextureCoords(0))
				for(UINT v = 0; v < pMesh->mNumVertices; v++){
					pVertData[v].t.x = pMesh->mTextureCoords[0][v].x;
					pVertData[v].t.y = pMesh->mTextureCoords[0][v].y;
				}
			//copy indexes
			if(pMesh->HasFaces())
				for(UINT f = 0; f < pMesh->mNumFaces; f++){
					auto& face = pMesh->mFaces[f];
					assert(face.mNumIndices == 3);
					memcpy(pIndexData + f*3, face.mIndices, sizeof(UINT)*3);
				}
			auto pObj = new Geometry;
			pObj->Load(pVertData, sizeof(MeshVertex), pMesh->mNumVertices, pIndexData, 3*pMesh->mNumFaces);
			pObj->m_world = box.GetMatrix();
			app.LoadGeometry(pObj, GEOMETRY_TYPE_MESH);
		}
		for(UINT i = 0; i < root->mNumChildren; i++)
			loadRecursively(root->mChildren[i]);
	};

	loadRecursively(scene->mRootNode);

	auto pLineVerts = new LineVertex[ARRAYSIZE(lineVerts)];
	auto pLineIndex = new UINT[ARRAYSIZE(lineIndex)];
	memcpy(pLineVerts, lineVerts, sizeof(lineVerts));
	memcpy(pLineIndex, lineIndex, sizeof(lineIndex));

	auto pObj = new Geometry;
	pObj->Load(pLineVerts, sizeof(LineVertex), ARRAYSIZE(lineVerts), pLineIndex, ARRAYSIZE(lineIndex));
	app.LoadGeometry(pObj, GEOMETRY_TYPE_LINE);

	app.LoadCamera(	Camera(	XMFLOAT3(0.0f, 0.0f, -2.0f),	XMFLOAT3(0.0f, 0.0f, 0.0f),	XMFLOAT3(0.0f, 1.0f, 0.0f)) );
	return app.MainLoop();
}